#include "selectsort.h"
#include <QScopeGuard>

SelectSort::SelectSort(QObject *parent) : SortBase{parent}
{
    animation.setStartValue(0.0);
    animation.setEndValue(1.0);
    animation.setEasingCurve(QEasingCurve::OutQuart);
    animation.setLoopCount(1);
    connect(&animation, &QVariantAnimation::finished, &loop, &QEventLoop::quit);
    connect(&animation, &QVariantAnimation::valueChanged, this, &SortBase::updateRequest);
}

void SelectSort::sort(size_t num_of_elem, int interval)
{
    // ensure calling setRunState(false) and update at the end of function
    auto guard = qScopeGuard([this] {
        setRunState(false);
        emit updateRequest();
    });

    stop();
    if (arr.size() != num_of_elem)
        randomArray(num_of_elem);
    setRunState(true);
    this->interval = interval;

    size_t &i = curr_i, &j = curr_j;
    for (i = 0; i < num_of_elem; i++) {
        min_index = i;
        for (j = i; j < num_of_elem; j++) {
            if (arr[j] < arr[min_index])
                min_index = j;

            animation.setDuration(this->interval);
            animation.start();
            loop.exec();
            if (!runState()) {
                return;
            }
            emit updateRequest();
        }

        if (min_index != i) {
            animation.setDuration(this->interval * 3);
            animation.start();
            swapFlag = true;
            loop.exec();
            if (runState()) {
                std::swap(arr[i], arr[min_index]);
                swapFlag = false;
            }
        }

        if (!runState()) {
            return;
        }
    }
}

void SelectSort::stop()
{
    setRunState(false);
    animation.stop();
    loop.quit();
    emit updateRequest();
}

void SelectSort::draw(QPainter *painter, int width, int height)
{
    painter->setPen(QColor(200, 200, 200));
    const size_t len = arr.size();
    const size_t max_elem = *(std::max_element(arr.begin(), arr.end()));

    // border
    constexpr int left_space = 0;
    constexpr int right_space = 0;
    constexpr int top_space = 20;
    constexpr int bottom_space = 60;

    // space width between two column
    constexpr int item_space = 10;

    const int text_height = painter->fontMetrics().height();

    // distance between column and number text
    constexpr int text_space = 15;

    // item width is dynamically adjusted according to the window width
    const double item_width = (width + item_space - left_space - right_space) / (double) len
                              - item_space;
    const double item_bottom = height - bottom_space;

    // item height is also dynamically adjusted according to the window height
    const double height_factor = (height - top_space - bottom_space) / (double) max_elem;

    // current drawing column width and height
    double item_left = 0;
    double item_height = 0;

    QColor color;
    for (size_t i = 0; i < len; i++) {
        // position of the column
        item_left = left_space + i * (item_width + item_space);
        item_height = height_factor * arr.at(i);

        // color of the column
        color = QColor(200, 200, 200);

        if (runState()) {
            if (i == curr_i) {
                color = QColor(255, 170, 0);
                if (swapFlag) {
                    item_left += animation.currentValue().toDouble() * (min_index - curr_i)
                                 * (item_width + item_space);
                }
            } else if (i == curr_j) {
                color = QColor(0, 170, 255);
            } else if (i < curr_i) {
                // each iteration will set one small number to it's final position
                color = QColor(0, 170, 0);
            }

            if (i == min_index) {
                if (swapFlag) {
                    item_left -= animation.currentValue().toDouble() * (min_index - curr_i)
                                 * (item_width + item_space);
                }

                const double traingle_top = item_bottom + text_height + text_space * 2;
                QPolygonF traingle;
                traingle << QPointF(item_left + item_width / 2.0, traingle_top)
                         << QPointF(item_left, traingle_top + item_width / 2.0)
                         << QPointF(item_left + item_width, traingle_top + item_width / 2.0);
                painter->setBrush(QBrush(QColor(0, 205, 102)));
                painter->drawPolygon(traingle);
            }
        }

        // draw text
        painter->drawText(item_left,
                          item_bottom + text_height + text_space,
                          QString::number(arr.at(i)));

        // draw rect
        painter->fillRect(item_left, item_bottom - item_height, item_width, item_height, color);
    }
}

void SelectSort::skip()
{
    this->interval = 1;
}
